package com.rock.platform.dao;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.persistence.Column;
import javax.persistence.Id;
import javax.persistence.NoResultException;
import javax.persistence.NonUniqueResultException;
import javax.persistence.Query;
import javax.persistence.Table;

import org.apache.commons.beanutils.BeanUtils;
import org.springframework.stereotype.Repository;

import com.rock.platform.dao.GenericDao;
import com.rock.platform.entity.GridParamsEntity;
import com.rock.platform.entity.PageEntity;
import com.rock.platform.exception.BaseException;
import com.rock.platform.exception.IExceptionMsgProcessor;
import com.rock.platform.util.EntityUtil;
import com.rock.platform.util.JpaDialectUtils;
import com.rock.platform.util.SessionUtils;
import com.rock.platform.util.SpringContextUtil;
import com.rock.platform.util.SqlConstructor;

@Repository
public class BaseDao extends GenericDao implements IBaseDao
{
    public <T> void add(T entity)
    {
    	try
        {
            super.add(entity);
            this.updateTableLog(entity);
        }
        catch(Exception e)
        {
        	e.printStackTrace();
            throw new BaseException(praseExceptionMsg(JpaDialectUtils.getException(e),e));            
        }
    }
    
    @SuppressWarnings("unchecked")
    public <T> void modify(T entity)
    {
    	 Map<String,Object> mSql = EntityUtil.buildUpdateSql(entity);
         this.execute(mSql.get("sql").toString(), (Map<String,Object>)mSql.get("paras"));
         this.updateTableLog(entity);
    }
    
    private <T> String getKeyField(Class<T> entityType)
    {
    	String keyField = "";
		for (Field field : entityType.getDeclaredFields())
		{
			if (field.getAnnotation(Id.class) != null)
			{
				Column c = field.getAnnotation(Column.class);
                if (c != null)
                {
                    if (!EntityUtil.isNullOrEmpty(c.name())) keyField = c.name();
                }
                else
                {
                	keyField = field.getName();
                }
                break;
			}
		}
		return keyField;
    }
    
    public <T> void remove(Object id, Class<T> entityType) {
    	
    	String tableName = entityType.getAnnotation(Table.class).name();
		StringBuffer sbSql = new StringBuffer("delete ");
		sbSql.append(tableName);
		sbSql.append(" where ");
		sbSql.append(getKeyField(entityType));
		sbSql.append(" = :id ");
		Map<String,Object> params = new HashMap<String,Object>();
		params.put("id", id);
		this.execute(sbSql.toString(), params);
	}

	public <T> void remove(Object[] ids, Class<T> entityType) {
		
		String tableName = entityType.getAnnotation(Table.class).name();
		StringBuffer sbSql = new StringBuffer("delete ");
		sbSql.append(tableName);
		sbSql.append(" where ");
		sbSql.append(getKeyField(entityType));
		sbSql.append(" in ( :ids ) ");
		Map<String,Object> params = new HashMap<String,Object>();
		params.put("ids", Arrays.asList(ids));
		this.execute(sbSql.toString(), params);
	}
	
    public Object findSingleResult(String strSql)
    {
        return findSingleResult(strSql, new HashMap<String, Object>());
    }

    public Object findSingleResult(String strSql, Map<String, ? extends Object> params)
    {
        Query query = this.getEntityManager().createNativeQuery(strSql);
        query = this.setParameter(query, params);
        try
        {
            return query.getSingleResult();
        }
        catch (NoResultException ex)
        {
            return null;
        }
        catch (NonUniqueResultException nex)
        {
        	nex.printStackTrace();
            throw new BaseException(praseExceptionMsg(JpaDialectUtils.getException(nex),nex)); 
        }
        catch(Exception e)
        {
        	e.printStackTrace();
            throw new BaseException(praseExceptionMsg(JpaDialectUtils.getException(e),e));            
        }
    }
    
    public <T> T findSingleResult(String strSql, Class<T> entityType)
    {
        return findSingleResult(strSql, entityType, null);       
    }
    
    
    public <T> T findSingleResult(String strSql, Class<T> entityType, Map<String, ? extends Object> params)
    {
        Query query = this.getEntityManager().createNativeQuery(strSql, entityType);        
        query = this.setParameter(query, params);
        
        try
        {
            @SuppressWarnings("unchecked")
            T result = (T) query.getSingleResult();
            return result;
        }
        catch (NoResultException ex)
        {
            return null;
        }
        catch (NonUniqueResultException nex)
        {
        	nex.printStackTrace();
        	throw new BaseException(praseExceptionMsg(JpaDialectUtils.getException(nex),nex));
        }
        catch(Exception e)
        {
        	e.printStackTrace();
            throw new BaseException(praseExceptionMsg(JpaDialectUtils.getException(e),e));
        }
    }

    public int execute(String strSql)
    {
        return execute(strSql, null);
    }

    public int execute(String strSql, Map<String, ? extends Object> params)
    {
        Query query = getEntityManager().createNativeQuery(strSql);
        query = this.setParameter(query, params);
        try
        {
            return query.executeUpdate();
        }
        catch(Exception e)
        {
        	e.printStackTrace();
        	throw new BaseException(praseExceptionMsg(JpaDialectUtils.getException(e),e));
        }
    }

	public <T> List<T> findByNativeSql(String strSql)
	{
		return this.findByNativeSql(strSql, new HashMap<String,Object>());
    }

    
    public <T> List<T> findByNativeSql(String strSql, Map<String, ? extends Object> params)
    {
        Query query = this.getEntityManager().createNativeQuery(strSql);
        query = this.setParameter(query, params);
        
        try
        {
        	@SuppressWarnings("unchecked")
            List<T> result = (List<T>)query.getResultList();
            
            return result;
        }
        catch(Exception e)
        {
        	e.printStackTrace();
        	throw new BaseException(praseExceptionMsg(JpaDialectUtils.getException(e),e));            
        }        
    }

    public <T> List<T> findByNativeSql(String strSql, Class<T> entityType)
    {
        return findByNativeSql(strSql, entityType, null);
    }

    public <T> List<T> findByNativeSql(String strSql, Class<T> entityType,
            Map<String, ? extends Object> params)
    {
        Query query = this.getEntityManager().createNativeQuery(strSql, entityType);
        query = this.setParameter(query, params);
        try
        {
            @SuppressWarnings("unchecked")
            List<T> result = (List<T>)query.getResultList();
            
            return result;
        }
        catch(Exception e)
        {
        	e.printStackTrace();
        	throw new BaseException(praseExceptionMsg(JpaDialectUtils.getException(e),e));            
        }          
    }

    public int getTotal(String strSql)
    {
        return getTotal(strSql, null);
    }

    public int getTotal(String strSql, Map<String, ? extends Object> params)
    {
    	StringBuffer sbSql = new StringBuffer("SELECT COUNT(0) FROM ( ");
    	sbSql.append(strSql);
    	sbSql.append(" ) t_tempdata");
		
        Query query = this.getEntityManager().createNativeQuery(sbSql.toString());
        query = this.setParameter(query, params);
        Object o = null;
        try
        {
            o = query.getSingleResult();
        }
        catch(Exception e)
        {
        	e.printStackTrace();
        	throw new BaseException(praseExceptionMsg(JpaDialectUtils.getException(e),e));            
        }  
        return Integer.parseInt(o.toString());
    }

    public <T> PageEntity findPageEntity(String strSql, GridParamsEntity gridParams, Class<T> entityType)
    {
        return findPageEntity(strSql, gridParams, entityType, null);
    }

    @SuppressWarnings("unchecked")
    public <T> PageEntity findPageEntity(String strSql, GridParamsEntity gridParams, Class<T> entityType,
            Map<String, Object> params)
    {
    	SqlConstructor sqlConstructor = new SqlConstructor(strSql,gridParams,entityType);
    	if (params != null) sqlConstructor.getFilterParams().putAll(params);

    	Query query = super.getEntityManager().createNativeQuery(sqlConstructor.getSql(), entityType);
    	query = this.setParameter(query,sqlConstructor.getFilterParams());
    	
        List<T> lst = null;
        try
        {
        	if (gridParams.getLimit() > 0)
        	{
        		query.setFirstResult(gridParams.getStart());
        		query.setMaxResults(gridParams.getLimit());
        	}
            lst = (List<T>) query.getResultList();
        }
        catch(Exception e)
        {
        	e.printStackTrace();
        	throw new BaseException(praseExceptionMsg(JpaDialectUtils.getException(e),e));            
        }  
        
        int total = getTotal(sqlConstructor.getSqlNoSort(), sqlConstructor.getFilterParams());

        PageEntity pageEntity = new PageEntity();
        pageEntity.setData(lst);
        pageEntity.setTotal(total);
        return pageEntity;
    }
    
	private Query setParameter(Query query, Map<String, ? extends Object> params)
    {
        if (params != null && params.size() > 0)
        {
            for (String key : params.keySet())
            {
                query.setParameter(key, params.get(key));
            }
        }
        return query;
    }

	private String praseExceptionMsg(String msg,Exception ex)
	{
		if (SpringContextUtil.containsBean("exceptionMsgProcessor"))
		{
			IExceptionMsgProcessor exceptionMsgProcessor = null;
			Object exceptionBean = SpringContextUtil.getBean("exceptionMsgProcessor");
			if(exceptionBean instanceof IExceptionMsgProcessor)
			{
				exceptionMsgProcessor = (IExceptionMsgProcessor)exceptionBean;
			}
			
			if (exceptionMsgProcessor == null)
			{
				return msg;
			}
			else
			{
				return exceptionMsgProcessor.praseExceptionMsg(msg, ex);
			}	
		}
		else
		{
			return msg;
		}
	}

	protected <T> void updateTableLog(T entity)
	{
		String tableName = entity.getClass().getAnnotation(Table.class).name();
		String keyName = "";
		String keyValue = "";
		for(Field f : entity.getClass().getDeclaredFields())
		{
			if (f.getAnnotation(Id.class) != null)
            {
                keyName = f.getName();
                Column c = f.getAnnotation(Column.class);
                if (c != null && c.name() != null)
                {
                	keyName = c.name();
                }
                try {
					keyValue = BeanUtils.getProperty(entity, f.getName());
				} catch (Exception e) {
					e.printStackTrace();
				}
                break;
            }
		}
		StringBuffer sbSql = new StringBuffer();
		sbSql.append("update ");
		sbSql.append(tableName);
		sbSql.append(" set lastUpdateDate = :lastUpdateDate, lastUpdateUserId = :lastUpdateUserId where ");
		sbSql.append(keyName);
		sbSql.append(" = :keyId ");
		Map<String,Object> params = new HashMap<String,Object>();
		params.put("lastUpdateDate", new Date());
		params.put("lastUpdateUserId", SessionUtils.getLoginBean().getUserId());
		params.put("keyId", keyValue);
		this.execute(sbSql.toString(), params);
	}
	
	protected static final int SUCCESS = 0; 
    /*
    private static BaseLogger logger()
    {
    	return BaseLogger.userLogger(BaseDao.class);
    }
    e.g. logger().debug("Exception: " + JpaDialectUtils.getException(e), BaseDao.class, "add(T entity)");
    */
}
